/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.xeerkat.xmpp;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import org.infoset.xml.Element;
import org.infoset.xml.Item;
import org.infoset.xml.ItemConstructor;
import org.infoset.xml.ItemDestination;
import org.infoset.xml.XMLException;

/**
 *
 * @author alex
 */
public class Discovery {

   public interface Notifier {
      void onDiscovery();
   }

   ClientConnection connection;
   StanzaRouter router;
   Map<String,List<String>> features;
   List<Notifier> notifiers;

   public Discovery(ClientConnection connection)
   {
      this(connection,null);
   }

   public Discovery(ClientConnection connection,StanzaRouter router)
   {
      this.connection = connection;
      this.router = router;
      this.features = new HashMap<String,List<String>>();
      this.notifiers = new ArrayList<Notifier>();
   }

   public void addNotifier(Notifier handler) {
      this.notifiers.add(handler);
   }

   public void removeNotifier(Notifier handler) {
      this.notifiers.remove(handler);
   }

   public String discover()
      throws XMLException
   {
      return discover(null);
   }

   public String discover(final ID to)
      throws XMLException
   {
      String id = "disco-"+UUID.randomUUID();

      if (router!=null) {
         List<String> theirFeatures = null;
         if (to!=null) {
            theirFeatures = features.get(to.toString());
         } else {
            theirFeatures = features.get("");
         }
         final List<String> discoveredFeatures = theirFeatures==null ? new ArrayList<String>() : theirFeatures;
         if (theirFeatures==null) {
            features.put(to==null ? "" : to.toString(),discoveredFeatures);
         }
         discoveredFeatures.clear();
         final AtomicReference<StanzaRouter.Route> routeRef = new AtomicReference<StanzaRouter.Route>();
         StanzaRouter.Route route = router.addRouteByAttribute("id", id, StanzaRouter.ComparisonType.Equals, new ItemDestination() {
            int level = 0;
            public void send(Item item) {
               switch (item.getType()) {
                  case ElementItem:
                     if (level==0) {
                        router.removeRoute(routeRef.get());
                     }
                     level++;
                     Element start = (Element)item;
                     if (start.getName().equals(Protocol.DISCO_INFO_FEATURE)) {
                        String var = start.getAttributeValue("var");
                        if (var!=null) {
                           discoveredFeatures.add(var);
                        }
                     }
                     break;
                  case ElementEndItem:
                     level--;
                     if (level==0) {
                        for (Notifier notifier : notifiers) {
                           notifier.onDiscovery();
                        }
                     }
               }
            }
         });
         routeRef.set(route);
      }
      ItemDestination stanza = connection.getStanzaDestination();
      ItemConstructor constructor = connection.getItemConstructor();
      Element iq = constructor.createElement(Protocol.IQ);
      if (to!=null) {
         iq.setAttributeValue("to", to.toString());
      }
      iq.setAttributeValue("id",id);
      iq.setAttributeValue("type","get");
      stanza.send(iq);
      stanza.send(constructor.createElement(Protocol.DISCO_INFO_QUERY));
      stanza.send(constructor.createElementEnd(Protocol.DISCO_INFO_QUERY));
      stanza.send(constructor.createElementEnd(Protocol.IQ));
      return id;
   }

   public List<String> getFeatures(ID to) {
      return getFeatures(to.toString());
   }

   public List<String> getFeatures(String to) {
      List<String> theirFeatures = features.get(to.toString());
      if (theirFeatures==null) {
         theirFeatures = Collections.emptyList();
      }
      return theirFeatures;
   }

   public List<String> getFeatures() {
      return getFeatures("");
   }

}
